use sea_orm::*;
use uuid::Uuid;
use chrono::Utc;
use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier};
use argon2::password_hash::{rand_core::OsRng, SaltString};

use crate::{
    database::Database,
    error::{AppError, AppResult},
    models::user::*,
};

#[derive(Clone)]
pub struct UserService {
    db: Database,
}

impl UserService {
    pub fn get_connection(&self) -> &sea_orm::DatabaseConnection {
        self.db.get_connection()
    }
    pub fn new(db: Database) -> Self {
        Self { db }
    }

    pub async fn create_user(&self, req: CreateUserRequest) -> AppResult<Model> {
        // Check if user already exists
        if self.find_by_email(&req.email).await?.is_some() {
            return Err(AppError::bad_request("User with this email already exists"));
        }

        if self.find_by_username(&req.username).await?.is_some() {
            return Err(AppError::bad_request("User with this username already exists"));
        }

        // Hash password
        let password_hash = self.hash_password(&req.password)?;

        let now = Utc::now().into();
        let user = ActiveModel {
            id: Set(Uuid::new_v4()),
            email: Set(req.email),
            username: Set(req.username),
            password_hash: Set(password_hash),
            first_name: Set(req.first_name),
            last_name: Set(req.last_name),
            role: Set(UserRole::User),
            is_active: Set(true),
            email_verified: Set(false),
            created_at: Set(now),
            updated_at: Set(now),
            last_login: Set(None),
        };

        let user = user.insert(self.db.get_connection()).await?;
        Ok(user)
    }

    pub async fn find_by_id(&self, id: Uuid) -> AppResult<Option<Model>> {
        let user = Entity::find_by_id(id)
            .one(self.db.get_connection())
            .await?;
        Ok(user)
    }

    pub async fn find_by_email(&self, email: &str) -> AppResult<Option<Model>> {
        let user = Entity::find()
            .filter(Column::Email.eq(email))
            .one(self.db.get_connection())
            .await?;
        Ok(user)
    }

    pub async fn find_by_username(&self, username: &str) -> AppResult<Option<Model>> {
        let user = Entity::find()
            .filter(Column::Username.eq(username))
            .one(self.db.get_connection())
            .await?;
        Ok(user)
    }

    pub async fn update_user(&self, id: Uuid, req: UpdateUserRequest) -> AppResult<Model> {
        let user = self.find_by_id(id).await?
            .ok_or_else(|| AppError::not_found("User not found"))?;

        // Check username uniqueness if being updated
        if let Some(ref username) = req.username {
            if let Some(existing) = self.find_by_username(username).await? {
                if existing.id != id {
                    return Err(AppError::bad_request("Username already taken"));
                }
            }
        }

        let mut user: ActiveModel = user.into();
        
        if let Some(username) = req.username {
            user.username = Set(username);
        }
        if let Some(first_name) = req.first_name {
            user.first_name = Set(Some(first_name));
        }
        if let Some(last_name) = req.last_name {
            user.last_name = Set(Some(last_name));
        }
        user.updated_at = Set(Utc::now().into());

        let user = user.update(self.db.get_connection()).await?;
        Ok(user)
    }

    pub async fn delete_user(&self, id: Uuid) -> AppResult<()> {
        let result = Entity::delete_by_id(id)
            .exec(self.db.get_connection())
            .await?;

        if result.rows_affected == 0 {
            return Err(AppError::not_found("User not found"));
        }

        Ok(())
    }

    pub async fn list_users(&self, page: u64, per_page: u64) -> AppResult<(Vec<Model>, u64)> {
        let paginator = Entity::find()
            .order_by_desc(Column::CreatedAt)
            .paginate(self.db.get_connection(), per_page);

        let total = paginator.num_items().await?;
        let users = paginator.fetch_page(page.saturating_sub(1)).await?;

        Ok((users, total))
    }

    pub async fn verify_password(&self, user: &Model, password: &str) -> AppResult<bool> {
        let parsed_hash = PasswordHash::new(&user.password_hash)
            .map_err(|e| AppError::internal(format!("Invalid password hash: {}", e)))?;

        let argon2 = Argon2::default();
        Ok(argon2.verify_password(password.as_bytes(), &parsed_hash).is_ok())
    }

    pub async fn change_password(&self, id: Uuid, req: ChangePasswordRequest) -> AppResult<()> {
        let user = self.find_by_id(id).await?
            .ok_or_else(|| AppError::not_found("User not found"))?;

        // Verify current password
        if !self.verify_password(&user, &req.current_password).await? {
            return Err(AppError::bad_request("Current password is incorrect"));
        }

        // Hash new password
        let new_password_hash = self.hash_password(&req.new_password)?;

        let mut user: ActiveModel = user.into();
        user.password_hash = Set(new_password_hash);
        user.updated_at = Set(Utc::now().into());

        user.update(self.db.get_connection()).await?;
        Ok(())
    }

    pub async fn update_last_login(&self, id: Uuid) -> AppResult<()> {
        let user = self.find_by_id(id).await?
            .ok_or_else(|| AppError::not_found("User not found"))?;

        let mut user: ActiveModel = user.into();
        user.last_login = Set(Some(Utc::now().into()));
        user.updated_at = Set(Utc::now().into());

        user.update(self.db.get_connection()).await?;
        Ok(())
    }

    pub async fn activate_user(&self, id: Uuid) -> AppResult<Model> {
        let user = self.find_by_id(id).await?
            .ok_or_else(|| AppError::not_found("User not found"))?;

        let mut user: ActiveModel = user.into();
        user.is_active = Set(true);
        user.updated_at = Set(Utc::now().into());

        let user = user.update(self.db.get_connection()).await?;
        Ok(user)
    }

    pub async fn deactivate_user(&self, id: Uuid) -> AppResult<Model> {
        let user = self.find_by_id(id).await?
            .ok_or_else(|| AppError::not_found("User not found"))?;

        let mut user: ActiveModel = user.into();
        user.is_active = Set(false);
        user.updated_at = Set(Utc::now().into());

        let user = user.update(self.db.get_connection()).await?;
        Ok(user)
    }

    fn hash_password(&self, password: &str) -> AppResult<String> {
        let salt = SaltString::generate(&mut OsRng);
        let argon2 = Argon2::default();
        
        let password_hash = argon2
            .hash_password(password.as_bytes(), &salt)
            .map_err(|e| AppError::internal(format!("Password hashing failed: {}", e)))?
            .to_string();

        Ok(password_hash)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::config::AppConfig;

    async fn setup_test_db() -> Database {
        // This would require a test database setup
        // For now, we'll skip actual database tests
        todo!("Setup test database")
    }

    #[tokio::test]
    #[ignore] // Ignore until test database is set up
    async fn test_create_user() {
        let db = setup_test_db().await;
        let service = UserService::new(db);

        let req = CreateUserRequest {
            email: "test@example.com".to_string(),
            username: "testuser".to_string(),
            password: "password123".to_string(),
            first_name: Some("Test".to_string()),
            last_name: Some("User".to_string()),
        };

        let user = service.create_user(req).await.unwrap();
        assert_eq!(user.email, "test@example.com");
        assert_eq!(user.username, "testuser");
    }
}